using System;
using UnityEngine.Rendering;

namespace UnityEngine.Recorder.Input
{
	public class CBRenderTextureInput : BaseRenderTextureInput
	{
		private struct CanvasBackup
		{
			public Camera camera;

			public Canvas canvas;
		}

		private bool m_ModifiedResolution;

		private Shader m_shCopy;

		private Material m_CopyMaterial;

		private TextureFlipper m_VFlipper = new TextureFlipper();

		private Mesh m_quad;

		private CommandBuffer m_cbCopyFB;

		private CommandBuffer m_cbCopyGB;

		private CommandBuffer m_cbClearGB;

		private CommandBuffer m_cbCopyVelocity;

		private Camera m_Camera;

		private bool m_cameraChanged;

		private Camera m_UICamera;

		private CanvasBackup[] m_CanvasBackups;

		public CBRenderTextureInputSettings cbSettings => (CBRenderTextureInputSettings)base.settings;

		public Camera targetCamera
		{
			get
			{
				return m_Camera;
			}
			set
			{
				if (m_Camera != value)
				{
					ReleaseCamera();
					m_Camera = value;
					m_cameraChanged = true;
				}
			}
		}

		public Shader copyShader
		{
			get
			{
				if (m_shCopy == null)
				{
					m_shCopy = Shader.Find("Hidden/Recorder/Inputs/CBRenderTexture/CopyFB");
				}
				return m_shCopy;
			}
			set
			{
				m_shCopy = value;
			}
		}

		public Material copyMaterial
		{
			get
			{
				if (m_CopyMaterial == null)
				{
					m_CopyMaterial = new Material(copyShader);
					copyMaterial.EnableKeyword("OFFSCREEN");
					if (cbSettings.m_AllowTransparency)
					{
						m_CopyMaterial.EnableKeyword("TRANSPARENCY_ON");
					}
				}
				return m_CopyMaterial;
			}
		}

		public override void BeginRecording(RecordingSession session)
		{
			if (cbSettings.m_FlipFinalOutput)
			{
				m_VFlipper = new TextureFlipper();
			}
			m_quad = CreateFullscreenQuad();
			switch (cbSettings.source)
			{
			case EImageSource.ActiveCameras:
			case EImageSource.MainCamera:
			case EImageSource.TaggedCamera:
			{
				int width = Screen.width;
				int height = Screen.height;
				if (cbSettings.m_CaptureUI)
				{
					GameObject gameObject = new GameObject();
					gameObject.name = "UICamera";
					gameObject.transform.parent = session.m_RecorderGO.transform;
					m_UICamera = gameObject.AddComponent<Camera>();
					m_UICamera.cullingMask = 32;
					m_UICamera.clearFlags = CameraClearFlags.Depth;
					m_UICamera.renderingPath = RenderingPath.DeferredShading;
					m_UICamera.targetTexture = base.outputRT;
					m_UICamera.enabled = false;
				}
				break;
			}
			default:
				throw new ArgumentOutOfRangeException();
			}
		}

		public override void NewFrameStarting(RecordingSession session)
		{
			switch (cbSettings.source)
			{
			case EImageSource.ActiveCameras:
				if (targetCamera == null)
				{
					GameObject gameObject = new GameObject();
					gameObject.name = "CameraHostGO-" + gameObject.GetInstanceID();
					gameObject.transform.parent = session.m_RecorderGO.transform;
					Camera camera = gameObject.AddComponent<Camera>();
					camera.clearFlags = CameraClearFlags.Nothing;
					camera.cullingMask = 0;
					camera.renderingPath = RenderingPath.DeferredShading;
					camera.targetDisplay = 0;
					camera.rect = new Rect(0f, 0f, 1f, 1f);
					camera.depth = float.MaxValue;
					targetCamera = camera;
				}
				break;
			case EImageSource.MainCamera:
				if (targetCamera != Camera.main)
				{
					targetCamera = Camera.main;
					m_cameraChanged = true;
				}
				break;
			case EImageSource.TaggedCamera:
			{
				string cameraTag = (base.settings as CBRenderTextureInputSettings).m_CameraTag;
				if (targetCamera == null || targetCamera.gameObject.tag != cameraTag)
				{
					try
					{
						GameObject[] array = GameObject.FindGameObjectsWithTag(cameraTag);
						if (array.Length > 0)
						{
							UnityEngine.Debug.LogWarning("More than one camera has the requested target tag:" + cameraTag);
						}
						targetCamera = array[0].transform.GetComponent<Camera>();
					}
					catch (UnityException)
					{
						UnityEngine.Debug.LogWarning("No camera has the requested target tag:" + cameraTag);
						targetCamera = null;
					}
				}
				break;
			}
			default:
				throw new ArgumentOutOfRangeException();
			}
			bool flag = PrepFrameRenderTexture();
			if (m_Camera != null && (m_cameraChanged || flag))
			{
				if (m_cbCopyFB != null)
				{
					m_Camera.RemoveCommandBuffer(CameraEvent.AfterEverything, m_cbCopyFB);
					m_cbCopyFB.Release();
				}
				int nameID = Shader.PropertyToID("_TmpFrameBuffer");
				m_cbCopyFB = new CommandBuffer
				{
					name = "Recorder: copy frame buffer"
				};
				m_cbCopyFB.GetTemporaryRT(nameID, -1, -1, 0, FilterMode.Bilinear);
				m_cbCopyFB.Blit(BuiltinRenderTextureType.CurrentActive, nameID);
				m_cbCopyFB.SetRenderTarget(base.outputRT);
				m_cbCopyFB.DrawMesh(m_quad, Matrix4x4.identity, copyMaterial, 0, 0);
				m_cbCopyFB.ReleaseTemporaryRT(nameID);
				m_Camera.AddCommandBuffer(CameraEvent.AfterEverything, m_cbCopyFB);
				m_cameraChanged = false;
			}
			if (Math.Abs(1f - targetCamera.rect.width) > float.Epsilon || Math.Abs(1f - targetCamera.rect.height) > float.Epsilon)
			{
				UnityEngine.Debug.LogWarning($"Recording output of camera '{targetCamera.gameObject.name}' who's rectangle does not cover the viewport: resulting image will be up-sampled with associated quality degradation!");
			}
		}

		public override void NewFrameReady(RecordingSession session)
		{
			if (cbSettings.m_CaptureUI)
			{
				Canvas[] array = Object.FindObjectsOfType<Canvas>();
				if (m_CanvasBackups == null || m_CanvasBackups.Length != array.Length)
				{
					m_CanvasBackups = new CanvasBackup[array.Length];
				}
				for (int i = 0; i < array.Length; i++)
				{
					Canvas canvas = array[i];
					if (canvas.isRootCanvas && canvas.renderMode == RenderMode.ScreenSpaceOverlay)
					{
						m_CanvasBackups[i].camera = canvas.worldCamera;
						m_CanvasBackups[i].canvas = canvas;
						canvas.renderMode = RenderMode.ScreenSpaceCamera;
						canvas.worldCamera = m_UICamera;
					}
					else
					{
						m_CanvasBackups[i].canvas = null;
					}
				}
				m_UICamera.Render();
				for (int j = 0; j < m_CanvasBackups.Length; j++)
				{
					if (!(m_CanvasBackups[j].canvas == null))
					{
						m_CanvasBackups[j].canvas.renderMode = RenderMode.ScreenSpaceOverlay;
						m_CanvasBackups[j].canvas.worldCamera = m_CanvasBackups[j].camera;
					}
				}
			}
			if (cbSettings.m_FlipFinalOutput)
			{
				m_VFlipper.Flip(base.outputRT);
			}
		}

		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				ReleaseCamera();
				UnityHelpers.Destroy(m_UICamera);
				if (m_VFlipper != null)
				{
					m_VFlipper.Dispose();
				}
			}
			base.Dispose(disposing);
		}

		protected virtual void ReleaseCamera()
		{
			if (m_cbCopyFB != null)
			{
				if (m_Camera != null)
				{
					m_Camera.RemoveCommandBuffer(CameraEvent.AfterEverything, m_cbCopyFB);
				}
				m_cbCopyFB.Release();
				m_cbCopyFB = null;
			}
			if (m_CopyMaterial != null)
			{
				UnityHelpers.Destroy(m_CopyMaterial);
			}
		}

		private bool PrepFrameRenderTexture()
		{
			if (base.outputRT != null)
			{
				if (base.outputRT.IsCreated() && base.outputRT.width == base.outputWidth && base.outputRT.height == base.outputHeight)
				{
					return false;
				}
				ReleaseBuffer();
			}
			base.outputRT = new RenderTexture(base.outputWidth, base.outputHeight, 0, RenderTextureFormat.ARGB32)
			{
				wrapMode = TextureWrapMode.Repeat
			};
			base.outputRT.Create();
			if (m_UICamera != null)
			{
				m_UICamera.targetTexture = base.outputRT;
			}
			return true;
		}

		public static Mesh CreateFullscreenQuad()
		{
			Vector3[] vertices = new Vector3[4]
			{
				new Vector3(1f, 1f, 0f),
				new Vector3(-1f, 1f, 0f),
				new Vector3(-1f, -1f, 0f),
				new Vector3(1f, -1f, 0f)
			};
			int[] triangles = new int[6]
			{
				0,
				1,
				2,
				2,
				3,
				0
			};
			Mesh mesh = new Mesh();
			mesh.vertices = vertices;
			mesh.triangles = triangles;
			return mesh;
		}
	}
}
